use common::service::sys_service_api::DXCDSLInfo;
use common::{
    base::{config::Config, status::Status},
    utils::{file_utils, time_utils},
};

use std::fs::File;
use std::io::Read;
use std::{collections::HashMap, sync::atomic::AtomicBool, time::Duration};
use tokio::{sync::Mutex, time::sleep};
use uuid::Uuid;

pub struct DownloadInfo {
    pub file_path: String,
    pub expire_time: i64,
}

pub struct UploadInfo {
    pub file_path: String,
    pub expire_time: i64,
    pub file_size: u64,
}

pub struct FileManager {
    root_path: String,
    is_close: AtomicBool,
    is_exit: AtomicBool,
    download_info_map: Mutex<HashMap<String, DownloadInfo>>,
    upload_info_map: Mutex<HashMap<String, UploadInfo>>,
}

impl FileManager {
    pub fn new(config: &Box<Config>) -> FileManager {
        let file_root_path = config.get_str("xport", "file-path").unwrap_or("./file");

        FileManager {
            root_path: String::from(file_root_path),
            is_exit: AtomicBool::new(false),
            is_close: AtomicBool::new(false),
            download_info_map: Mutex::new(HashMap::new()),
            upload_info_map: Mutex::new(HashMap::new()),
        }
    }

    pub fn get_file_path(&self, dxc_name: &str, file_path: &str) -> String {
        format!("{}/{}/{}", self.root_path, dxc_name, file_path)
    }

    pub fn get_md5(&self, file_path: &str) -> Result<String, Status> {
        let md5 = file_utils::get_md5(file_path);
        if md5.is_err() {
            Err(Status::error(md5.err().unwrap().to_string()))
        } else {
            Ok(md5.unwrap())
        }
    }

    pub fn get_dxc_dsl(&self, dxc_path: &str) -> Result<DXCDSLInfo, Status> {
        let md5 = self.get_md5(dxc_path)?;
        let input =
            File::open(&dxc_path).expect(&format!("打开文件失败，请检查文件{}是否存在", &dxc_path));
        let zip = zip::ZipArchive::new(input);
        if zip.is_err() {
            return Err(Status::error(format!(
                "打开文件失败，请检查文件:{} 是否是 DXC 文件",
                dxc_path
            )));
        }
        let mut zip = zip.unwrap();
        let proto_file = zip.by_name("source_api.proto");
        if let Err(err) = proto_file {
            return Err(Status::error(format!("获取 dsl 信息失败：{:?}", err)));
        }
        let mut zip_file = proto_file.unwrap();
        let mut dsl_info = DXCDSLInfo::default();
        let result = zip_file.read_to_string(&mut dsl_info.dsl);
        dsl_info.md5 = md5;
        if let Err(err) = result {
            return Err(Status::error(format!("读取 dsl 信息失败：{:?}", err)));
        }
        Ok(dsl_info)
    }

    /**
     *
     */
    pub async fn is_file_exist(&self, dxc_name: &str, file_path: &str) -> bool {
        let real_path = self.get_file_path(dxc_name, file_path);
        file_utils::is_file_exist(&real_path).await
    }
    /**
     *
     */
    pub async fn is_dir_exist(&self, dxc_name: &str, dir_path: &str) -> bool {
        let real_path: String = self.get_file_path(dxc_name, dir_path);
        file_utils::is_dir_exist(&real_path).await
    }

    pub async fn create_dir(&self, dxc_name: &str, dir_path: &str) -> bool {
        let real_path: String = self.get_file_path(dxc_name, dir_path);

        file_utils::create_dir(&real_path).await
    }

    pub async fn create_file(&self, dxc_name: &str, file_path: &str) -> bool {
        let real_path: String = self.get_file_path(dxc_name, file_path);

        file_utils::create_file(&real_path).await
    }

    pub async fn write_content_to_file(
        &self,
        dxc_name: &str,
        file_path: &str,
        file_content: &str,
    ) -> std::io::Result<()> {
        let real_path: String = self.get_file_path(dxc_name, file_path);

        file_utils::write_content_to_file(&real_path, file_content).await
    }

    pub async fn add_download_info(&self, download_info: DownloadInfo) -> String {
        let download_key = Uuid::new_v4().to_string().replace("-", "");

        let mut download_info_map = self.download_info_map.lock().await;

        download_info_map.insert(download_key.clone(), download_info);

        download_key
    }

    pub async fn add_upload_info(&self, upload_info: UploadInfo) -> String {
        let upload_key = Uuid::new_v4().to_string().replace("-", "");

        let mut upload_info_map = self.upload_info_map.lock().await;

        upload_info_map.insert(upload_key.clone(), upload_info);

        upload_key
    }

    pub async fn get_and_remove_download_info(&self, download_key: &str) -> Option<DownloadInfo> {
        let mut download_info_map = self.download_info_map.lock().await;

        download_info_map.remove(download_key)
    }

    pub async fn get_and_remove_upload_info(&self, upload_key: &str) -> Option<UploadInfo> {
        let mut upload_info_map = self.upload_info_map.lock().await;

        upload_info_map.remove(upload_key)
    }

    pub async fn check_and_delete_expire_info(&self) {
        while !self.is_close.load(std::sync::atomic::Ordering::Relaxed) {
            sleep(Duration::from_millis(3000)).await;

            let cur_timestamp = time_utils::cur_timestamp();
            {
                let mut upload_info_map = self.upload_info_map.lock().await;

                upload_info_map.retain(|_key, upload_info| upload_info.expire_time > cur_timestamp);
            }

            {
                let mut download_info_map = self.download_info_map.lock().await;

                download_info_map
                    .retain(|_key, download_info| download_info.expire_time > cur_timestamp);
            }
        }

        self.is_exit
            .store(false, std::sync::atomic::Ordering::Relaxed);
    }
}
